問題解説 Web(改ざんされました!) 実技

解説

問題文

弊社のコーポレートサイトに以下のような文言が追加されてしまいました。

  • コーポレートサイトのURL: http://qew.example/
    • VNCサーバのブラウザまたはcurl等で閲覧可能です
    • ブラウザの起動方法: デスクトップ上で右クリック → Applications → Web Browser
  • 追加された文言(少し違うときもあります):
    • 激安コピーバッグ example.com 激安コピー時計 example.com 激安コピーアクセサリー example.com

内容に心当たりはないので、改ざんされてしまったようです。
該当ファイルを編集して元に戻しましたが、すぐにまた改ざんされてしまいます……。
(是非試してみてください……該当行を消しても、しばらくすると復活してしまうんです……)
サーバがハッキングされているのでしょうか??大至急、原因を特定して直してください!!
また、同じ手口で改ざんされないような対処もお願いします。

サーバ上のファイルや設定はどのように変更いただいて問題ありませんが、
現在も一般の利用者様が利用しているので、できるだけ閲覧に影響を及ぼさないようにしてください!!

構成:

  • [Webサーバ (192.168.0.33)] – [ロードバランサ (192.168.0.200)] – [インターネット] – [Webサイトの利用者]

    • 出題上の踏み台となる[VNCサーバ]から[Webサーバ]にSSH接続が可能です。
      • [Webサーバ]のファイルが改ざんされてしまっています
      • [ロードバランサ]へのSSH接続は許可されていません。
    • Webサイトの利用者は、必ず[ロードバランサ]を経由して接続します
      • [インターネット]から[ロードバランサ]を経由しない接続は許可されていません
      • 将来的に分散構成にする予定でしたが、今のところ[Webサーバ]は1台です

WebサーバのSSH認証情報

VNCサーバから以下の認証情報で接続可能です。

  • IPアドレス: 192.168.0.33
  • ユーザ名: centos
  • パスワード: ********

※ VNCサーバ自身にも直接SSH接続可能です。VNC経由のTerminalで操作するよりオススメです。

出題に伴うメタ的な注意事項

  • 上記の[インターネット]と[Webサイトの利用者]は出題のための架空の存在ですが、アクセスは発生させています
    • それらしく振る舞いますが多少の違和感には目をつぶってください
    • 以下の例示用IPアドレスを利用していますが、国内外のISPが保有するセグメント(つまり普通のグローバルIPアドレス)として読み替えてください
      • 192.0.2.0/24
      • 198.51.100.0/24
      • 203.0.113.0/24
  • 採点は「問題文」にて提示した依頼内容を満たしているか自動で行います
    • サーバの状況のみで採点を行います
    • 対応完了の報告は必要ありません

解答・解説

発生していた問題

根本的には、お問い合わせフォームに設置されていたアップローダに脆弱性がありました。

# "/var/www/wordpress/wp-contact/contact.php"より抜粋
$tmpfile = $_FILES['input-file']['tmp_name'];
$filename = $_FILES['input-file']['name'];

// あぶない拡張子を判定して、実行されないように拒否する
if (preg_match('/\.(php|shtml|cgi)$/', $filename)) {
  http_response_code(400);
  exit('ERROR: 実行可能ファイルのアップロードは禁止されています。');
}

// アップロードされた一時ファイルをfiles/以下に保存する
move_uploaded_file($tmpfile, 'files/' . $filename);

非常に雑に実装されている為、複数の脆弱性を抱えている可能性がありますが、今回攻撃に利用されたのは拡張子判定の部分です。
ブラックリスト方式で拒否していますが、「.htaccess」が含まれていない為、以下のファイルをアップロードされてしまいました。

/var/www/wordpress/wp-contact/files/.htaccess
<FilesMatch "screenshots-.*.jpg$">
SetHandler application/x-httpd-php
</FilesMatch>

結果として、/screenshots-.*\.jpg$/にマッチするファイルはphpとして実行されるようになり、かつjpgに対するアップロード制限は存在しない為、拡張子を偽装したphpをアップロードしGET/POSTリクエストを行うことで、任意のphpコードを実行可能な状態となっていました。
ただし、上記を利用して直接改ざんを行うと、アクセスログ等からすぐに判明してしまいます。その為、今回の攻撃者は上記の脆弱性を利用して予め踏み台を用意していました。
具体的には、POSTされた内容をeval()するだけのphpファイルです。これを以下の3箇所に設置しており、必要に応じて使い分けていました。

/var/www/wordpress/wp-content/upgrade/index.php
/var/www/wordpress/wp-includes/feed-xml.php
/var/www/wordpress/wp-content/plugins/world.php

また、任意のphpコードを実行できるということは、phpのsystem()も利用できますので、phpの実行ユーザ権限で任意のシェルコードを実行できる状態となってしまいました。今回はphpがapacheの実行ユーザ権限で動作していた為、DocumentRoot以下のファイルは基本的に改ざん可能な状況でした。
具体的に改ざん対象となったファイルはWordPressテーマのヘッダ末尾となります。

// /var/www/wordpress/wp-content/themes/lightning/header.php より抜粋
    <?php do_action( 'lightning_header_append' ); ?>
</header>
<?php do_action( 'lightning_header_after' ); ?>
<h1>激!激!激!激安コピーバッグ example.com 激安コピー時計 example.com 激安コピーアクセサリー example.com</h1>

解答例

本問題では複数の解答が考えられます。以下に作問者が想定していた解法を示します。
まずはじめに、改ざん箇所を見つけて削除します。
Webページを目視にて確認すると、ヘッダ末尾付近が改ざんされているとわかります。
WordPressにて構築されているため、構成を把握している人はテーマのheader.phpに直接たどり着けるかもしれませんが、わからなくともgrepで検索すると特定が可能です。

# grep -r "激安" /var/www/wordpress/wp-content/
/var/www/wordpress/wp-content/themes/lightning/header.php:<h1>激!激!激!激安コピーバッグ example.com 激安コピー時計 example.com 激安コピーアクセサリー example.com</h1>

今回、ファイルの編集に関して特に禁止事項は無いため、vi等で該当行を削除すれば最初の改ざんは対処出来ます。
対応後、数十秒待つと再び改ざんされてしまいます。原因の特定方法は複数考えられますが、Webアクセス経由を疑う場合はアクセスログを調べると良いでしょう。
まず、header.phpのタイムスタンプから改ざんが発生した時刻を調べます。

# stat /var/www/wordpress/wp-content/themes/lightning/header.php`
  File: `/var/www/wordpress/wp-content/themes/lightning/header.php'
  Size: 1508            Blocks: 8          IO Block: 4096   通常ファイル
Device: fd03h/64771d    Inode: 522539      Links: 1
Access: (0644/-rw-r--r--)  Uid: (   48/  apache)   Gid: (   48/  apache)
Access: 2018-12-17 11:56:06.206341004 +0900
Modify: 2018-12-17 11:56:05.522324860 +0900
Change: 2018-12-17 11:56:05.522324860 +0900
 Birth: -

そして、該当する時刻のアクセスログを調べます。(時刻は多少前後する場合があります)

# grep "17/Dec/2018:11:56:05" /var/log/httpd/access_log 
192.0.2.35 192.168.0.200 - - [17/Dec/2018:11:56:05 +0900] "POST /wp-content/upgrade/ HTTP/1.0" 200 - "http://qew.example/" "Mozilla/5.0 (Windows NT 10.0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/64.0.3282.140 Safari/537.36 Edge/17.17134"
203.0.113.153 192.168.0.200 - - [17/Dec/2018:11:56:05 +0900] "GET /company/ HTTP/1.0" 200 9466 "-" "Mozilla/5.0 (Windows NT 10.0; Win64; rv:33.1) Gecko/20100101 Firefox/33.1"

該当時間帯にアクセスのあったファイルを調べると、"/wp-content/upgrade/" つまり "/var/www/wordpress/wp-content/upgrade/index.php" にPOSTリクエストが届いており、中身を見ると明らかに不審であることがわかります。
該当ファイルを削除の上、改ざん箇所を再度直して様子を見ましょう。
……数十秒後、再び改ざんされます。
同様の方法でも対応が可能ですが、同じファイルが複数仕掛けられている能性がありますので、grepしてみましょう。

# grep -lr "\$_POST\['cmd'\]" /var/www/wordpress/
/var/www/wordpress/wp-content/plugins/world.php
/var/www/wordpress/wp-content/upgrade/index.php
/var/www/wordpress/wp-includes/feed-xml.php

未発見のファイルが2つ見つかりました。また、消したはずのファイルも復活しています。
全て削除の上、改ざん箇所を再度直して様子を見ましょう。
……数十秒後、再び改ざんされます。
また、/var/www/wordpress/以下のタイムスタンプが全て更新されてしまいました。隠したいファイルがあるのでしょうか。
調べると消したはずの/wp-content/plugins/world.phpが復活していました。

# grep -lr "\$_POST\['cmd'\]" /var/www/wordpress/
/var/www/wordpress/wp-content/plugins/world.php

“world.php”を設置するコードが存在しないかgrepすると、なぜかjpgファイルが見つかります。
fileコマンドで調べると実態はphpのようです。アクセスログを調べるとGETリクエストが来ています。

# grep -lr "/world.php" /var/www/wordpress/
/var/www/wordpress/wp-contact/files/screenshots-20181121-231123.jpg

# file /var/www/wordpress/wp-contact/files/screenshots-20181121-231123.jpg
/var/www/wordpress/wp-contact/files/screenshots-20181121-231123.jpg: PHP script, UTF-8 Unicode text, with very long lines

# grep screenshots-20181121-231123.jpg /var/log/httpd/access_log 
192.0.2.24 192.168.0.200 - - [17/Dec/2018:12:11:09 +0900] "GET /wp-contact/files/screenshots-20181121-231123.jpg HTTP/1.0" 200 - "http://qew.example/wp-contact/contact.php" "Mozilla/5.0 (Windows NT 6.3; Win64; x64; rv:62.0) Gecko/20100101 Firefox/62.0"

拡張子jpgはphpとして実行されない設定が普通です。ですが、同じ階層に不審な.htaccessが設置されています。このファイルがjpgを実行可能にしているようです。

# cat /var/www/wordpress/wp-contact/files/.htaccess
<FilesMatch "screenshots-.*\.jpg$">
  SetHandler application/x-httpd-php
</FilesMatch>

該当するファイルを全て消して、改ざん箇所を直して様子を見ましょう。
……数十秒後、再び改ざんされます。また、.htaccessも復活しています。どうやら攻撃者は.htaccess自体を設置する術を持っているようです。
そもそもこのディレクトリは何なのでしょうか?一つ上の階層を見ると、問い合わせフォームに付属するアップローダがファイルを保存する場所のようです。

# grep -lr "/world.php" /var/www/wordpress/
/var/www/wordpress/wp-contact/files/screenshots-20181121-231123.jpg

# file /var/www/wordpress/wp-contact/files/screenshots-20181121-231123.jpg
/var/www/wordpress/wp-contact/files/screenshots-20181121-231123.jpg: PHP script, UTF-8 Unicode text, with very long lines

# grep screenshots-20181121-231123.jpg /var/log/httpd/access_log 
192.0.2.24 192.168.0.200 - - [17/Dec/2018:12:11:09 +0900] "GET /wp-contact/files/screenshots-20181121-231123.jpg HTTP/1.0" 200 - "http://qew.example/wp-contact/contact.php" "Mozilla/5.0 (Windows NT 6.3; Win64; x64; rv:62.0) Gecko/20100101 Firefox/62.0"

一応、.phpはアップロードできないように判定されていますが、.htaccessについてはチェックされないようです。
簡単に.htaccessも拒否されるように判定を追加してみましょう。

# grep htaccess /var/www/wordpress/wp-contact/contact.php 
if (preg_match('/\.(php|shtml|cgi|htaccess)$/', $filename)) {

先程消したファイルを全て消して、改ざん箇所を直して様子を見ます。
……改ざんされなくなりました!
試行中に復活しているファイルがある場合は忘れずに消しておきます。

# grep -lr "\$_POST\['cmd'\]" /var/www/wordpress/
/var/www/wordpress/wp-content/plugins/world.php

以上で対応終了です。

採点基準

本問題では問題文に記載の通り、自動採点を行いました。採点箇所は以下です。

  • 加点ポイント

    • トップページに「株式会社QWE」が含まれる
    • トップページに「<head>」が含まれる
    • お問い合わせフォームが閲覧出来る
    • お問い合わせフォームにテストファイルがアップロード出来る
  • 減点ポイント

    • トップページに「激!激!激!」が含まれる
      • 初期状態にのみ含まれる為、一度でも改ざん箇所を消していれば回避されます
    • トップページに「激安コピーバッグ」が含まれる
    • http://qew.example/wp-content/upgrade/ にてsystem('uptime')が実行可能
    • http://qew.example/wp-includes/feed-xml.php にてsystem('uptime')が実行可能
    • http://qew.example/wp-content/plugins/world.php にてsystem('uptime')が実行可能
    • アップローダ経由でsystem('uptime')が実行可能
  • 無停止ボーナス

    • 監視アクセスログのエラー行数が少なければ加点
      • 他の点数が0点となったチームには加点していません

改ざんを防ぐ方法については問いません。万能の攻撃者を想定してしまうと他の脆弱性が存在する可能性もあることから、上記項目のみで採点としています。

出題の仕組みについて

アクセスログについて

本問題では予め偽装したログファイルを設置するのではなく、リアルタイムに一般ユーザ及び攻撃者のHTTPリクエストを発生させていました。
IPアドレスについては”X-Forwarded-For”ヘッダを偽装し例示用IPアドレスに、UAも予め用意しておいたパターンから偽装しました。
同じIPアドレスは必ず同じUAに、また善良なユーザが突然攻撃者にならないよう、予めリスト化したものからランダムに利用しています。

==> scripts/assets/ip-attacker.txt <==
192.0.2.11 Mozilla/5.0 (Windows NT 6.3; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.36
...

==> scripts/assets/ip-dummy.txt <==
192.0.2.72 python-requests/2.18.4
...

==> scripts/assets/ip-user.txt <==
203.0.113.99 Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/50.0.2661.102 UBrowser/6.1.2015.1007 Safari/537.36
...
http://qew.example/yao.php
http://qew.example/editor/fckeditor.js
http://qew.example/web.config
http://qew.example/sql/php-myadmin/index.php?lang=en
http://qew.example/51.php
http://qew.example/wwb/index.php?module=site&#038;show=home
http://qew.example//pma/scripts/setup.php
http://qew.example/static/home/css/common.css
http://qew.example/index.htm
http://qew.example/xx.php

ip-dummy.txtは比較的不審なUAを利用しています。アクセス先も以下のような「なんとなく不審なURL」でした。本問題では特に意味はなく、実際の攻撃によるアクセスログを隠すためのダミーアクセスです。
http://qew.example/yao.phphttp://qew.example/yao.php

攻撃リクエストについて

本問題では改ざんを直しても、すぐに改ざんされてしまう状況を作りました。
これはロードバランサ内に設置したスクリプトが毎分トップページを監視しており、改ざん箇所が消えている場合のみ以下の手順で改ざんを試みます。

1, "http://qew.example/wp-content/upgrade/" による改ざん
2, "http://qew.example/wp-includes/feed-xml.php" による改ざん 及び、/wp-content/upgrade/index.php の復元
3, アップローダによる改ざん 及び、/wp-content/plugins/world.php の復元

http://qew.example/wp-content/plugins/world.php については、今回の攻撃スクリプトから利用されていません。これは、攻撃が完全に防がれてしまった場合に、後日改めて利用する目的で設置している為です。
攻撃に利用するとアクセスログ等から存在が露見してしまうため、今回は利用しないのです。このファイルも消しておかないと、数日後に再発してしまいます。

その他

本問題で発生した攻撃はフィクションですが、現実に発生した事案を元に作成しています。特に踏み台として設置されるphpは、現実では難読化や様々な機能を追加した「WebShell」と呼ばれるものが利用されます。比較的「よくある」サイバー攻撃なので、是非覚えておいてください。